package com.lee.redisplus.util;

import io.netty.handler.codec.Delimiters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.xml.crypto.dsig.keyinfo.KeyValue;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author jackielee
 * @version 1.0
 * @description RedisUtil
 * @date 2021/4/9
 **/
@Component
public class RedisUtil {

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 缓存过期
     *
     * @param key  键
     * @param time 秒
     * @return
     */
    public boolean expire(String key, long time) {
        return TryDo.doing(() -> {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        });
    }

    /**
     * 获取key过期时间
     *
     * @param key 键
     * @return 秒，0为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 是否存在key
     *
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        return TryDo.doing(() -> redisTemplate.hasKey(key));
    }

    /**
     * 删除缓存
     *
     * @param key
     */
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    /***************** string *************************/

    /**
     * 获取缓存
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 设置缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(String key, Object value) {
        return TryDo.doing(() -> {
            redisTemplate.opsForValue().set(key, value);
            return true;
        });
    }

    /**
     * 设置缓存及过期时间
     *
     * @param key
     * @param value
     * @param time  秒
     * @return
     */
    public boolean set(String key, Object value, long time) {
        return TryDo.doing(() -> {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        });
    }

    /**
     * 递增
     *
     * @param key
     * @param delta 大于0数
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 大于0
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().decrement(key, delta);
    }

    /***************** map **********************/

    /**
     * Hash Get
     *
     * @param key  键 非null
     * @param item 项 非null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hash key对应所有键值
     *
     * @param key
     * @return 多键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     *
     * @param key 键
     * @param map 多键值
     * @return
     */
    public boolean hmset(String key, Map<String, Object> map) {
        return TryDo.doing(() -> {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        });
    }

    /**
     * HashSet 及过期时间
     *
     * @param key
     * @param map
     * @param time 秒
     * @return
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        return TryDo.doing(() -> {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        });
    }

    /**
     * 向hash中入入数据，不存在则创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return
     */
    public boolean hset(String key, String item, Object value) {
        return TryDo.doing(() -> {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        });
    }

    /**
     * 向hash放入数据，不存在则创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  秒
     * @return
     */
    public boolean hset(String key, String item, Object value, long time) {
        boolean res = hset(key, item, value);
        if (res && time > 0) {
            res &= expire(key, time);
        }
        return res;
    }

    /**
     * 删除hash中值
     *
     * @param key  键
     * @param item 项多个 非null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash中是否有该项值
     *
     * @param key  键 非null
     * @param item 项 非null
     * @return
     */
    public boolean hHashKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 不存在，则创建一个并返回增后值
     *
     * @param key  键
     * @param item 项
     * @param by   增大量 大于0
     * @return
     */
    public double incr(String key, String item, double by) {
        by = by > 0 ? by : -by;
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   减 小于0
     * @return
     */
    public double hdecr(String key, String item, double by) {
        by = by > 0 ? -by : by;
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    /******************** set ****************************/

    /**
     * 据key获取set中所有值
     *
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 据value从set中是否存在
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean sHasKey(String key, Object value) {
        return TryDo.doing(() -> redisTemplate.opsForSet().isMember(key, value));
    }

    /**
     * 将数据存入set缓存
     *
     * @param key    键
     * @param values 多值
     * @return
     */
    public long sSet(String key, Object... values) {
        return TryDo.doing(() -> redisTemplate.opsForSet().add(key, values));
    }

    /**
     * 数据存入set并设置过期时间
     *
     * @param key    键
     * @param time   秒
     * @param values 多值
     * @return
     */
    public long sSet(String key, long time, Object... values) {
        long res = sSet(key, values);
        if (res > 0 && time > 0) {
            expire(key, time);
        }
        return res;
    }

    /**
     * 获取set缓存长度
     *
     * @param key 键
     * @return
     */
    public long sSize(String key) {
        return TryDo.doing(() -> redisTemplate.opsForSet().size(key));
    }

    /**
     * 移除值为values的项
     *
     * @param key    键
     * @param values 多值
     * @return
     */
    public long sRemove(String key, Object... values) {
        return TryDo.doing(() -> redisTemplate.opsForSet().remove(key, values));
    }

    /************************** list ****************************/

    /**
     * 获取list缓存内容
     *
     * @param key   键
     * @param start 开始索引
     * @param end   结束索引  0~-1则取所有
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        return TryDo.doing(() -> redisTemplate.opsForList().range(key, start, end));
    }

    /**
     * 通过索引获取值
     *
     * @param key   键
     * @param index 索引  >=0时，0表头，1第二元素……；index<0时，-1表尾，-2倒二元素,……
     * @return
     */
    public Object lGet(String key, int index) {
        return TryDo.doing(() -> redisTemplate.opsForList().index(key, index));
    }

    /**
     * 存入list
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        return TryDo.doing(() -> {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        });
    }

    /**
     * 存入list，及过期
     *
     * @param key   键
     * @param value 值
     * @param time  秒
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        boolean res = lSet(key, value);
        if (res && time > 0) {
            res &= expire(key, time);
        }
        return res;
    }

    /**
     * list存入list缓存
     *
     * @param key    键
     * @param values 列表
     * @return
     */
    public boolean lSet(String key, List<Object> values) {
        return TryDo.doing(() -> {
            redisTemplate.opsForList().rightPushAll(key, values);
            return true;
        });
    }

    /**
     * list存入list缓存，过期
     *
     * @param key    键
     * @param values 键
     * @param time   秒
     * @return
     */
    public boolean lSet(String key, List<Object> values, long time) {
        boolean res = lSet(key, values);
        if (res && time > 0) {
            res &= expire(key, time);
        }
        return res;
    }

    /**
     * 据索引修改list某数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdate(String key, long index, Object value) {
        return TryDo.doing(() -> {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        });
    }

    /**
     * 移除n个值为value
     * @param key 键
     * @param count 数量
     * @param value 值
     * @return 移除个数
     */
    public long lRemove(String key, long count, Object value){
        return TryDo.doing(()-> redisTemplate.opsForList().remove(key, count, value));
    }

    /***************************** 有序集合 sort set ***************************/

    /**
     * 有序set添加元素
     * @param key 键
     * @param value 值
     * @param score
     * @return
     */
    public boolean zSet(String key, Object value, double score){
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 有序set多元素
     * @param key
     * @param types
     * @return
     */
    public long zSet(String key, Set<ZSetOperations.TypedTuple> types){
        return redisTemplate.opsForZSet().add(key, types);
    }

    /**
     * 有序set元素递增
     * @param key
     * @param value
     * @param delta
     */
    public void  zIncrementScore(String key, Object value, long delta){
        redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    public void zUnionAndStore(String key, Collection otherKeys, String destKey){
        redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 获取zset数量
     * @param key 键
     * @param value 值
     * @return
     */
    public long getZSetScore(String key, Object value){
        Double score = redisTemplate.opsForZSet().score(key, value);
        return score == null ? 0 : score.longValue();
    }

    /**
     * 获取有序集key中成员member排名
     * 其中有序集成员按score值递减（大到小）排序
     * @param key 键
     * @param start 开始
     * @param end 结束
     * @return
     */
    public Set<ZSetOperations.TypedTuple> getZSetRank(String key, long start, long end){
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, start, end);
    }
}
